#pragma once
#include <functional>

namespace krryn{
namespace core{
	//
	// mem_fun_* clone for pointer types.
	// 
	// usage:
	//   mem_fun_ptr<ptr_type>(&some_class::some_method)
	//
	// This exists to complement refc_ptr, but works on other 
	// pointer types as well.
	//

	template<typename _Result, typename _Ty, typename _Ptr = _Ty*>
	class mem_fun_t : public std::unary_function<_Ptr, _Result>{
	public:
		explicit mem_fun_t(result_type (_Ty::* a_Func)()) : m_Func(a_Func) {}

		result_type operator()(argument_type a_Arg){
			return (*a_Arg.*m_Func)();
		}
	private:
		result_type (_Ty::* m_Func)();
	};

	template<typename _Result, typename _Ty, typename _Ptr = const _Ty*>
	class const_mem_fun_t : public std::unary_function<_Ptr, _Result>{
	public:
		explicit const_mem_fun_t(const result_type (_Ty::* a_Func)()) : m_Func(a_Func) {}

		result_type operator()(argument_type a_Arg) const{
			return (*a_Arg.*m_Func)();
		}
	private:
		const result_type (_Ty::* m_Func)();
	};

	template<typename _Result, typename _Ty, typename _Arg, typename _Ptr = _Ty*>
	class mem_fun1_t : public std::binary_function<_Ptr, _Arg, _Result>{
	public:
		explicit mem_fun1_t(result_type (_Ty::* a_Func)(_Arg)) : m_Func(a_Func) {}

		result_type operator()(first_argument_type a_Arg, _Arg a_Value){
			return (*a_Arg.*m_Func)(a_Value);
		}
	private:
		result_type (_Ty::* m_Func)(_Arg);
	};

	template<typename _Result, typename _Ty, typename _Arg, typename _Ptr = const _Ty*>
	class const_mem_fun1_t : public std::binary_function<_Ptr, _Arg, _Result>{
	public:
		explicit const_mem_fun1_t(const result_type (_Ty::* a_Func)(_Arg)) : m_Func(a_Func) {}
		
		result_type operator()(first_argument_type a_Arg, _Arg a_Value) const{
			return (*a_Arg.*m_Func)(a_Value);
		}
	private:
		const result_type (_Ty::* m_Func)(_Arg);
	};

	template<typename _Ptr,
		typename _Result,
		typename _Ty> inline
		mem_fun_t<_Result, _Ty, _Ptr>
			mem_fun_ptr(_Result (_Ty::* a_Func)())
	{
		return mem_fun_t<_Result, _Ty, _Ptr>(a_Func);
	}

	template<typename _Ptr,
		typename _Result, 
		typename _Ty> inline
		const_mem_fun_t<_Result, _Ty, _Ptr> 
			mem_fun_ptr(const _Result (_Ty::* a_Func)())
	{
		return const_mem_fun_t<_Result, _Ty, _Ptr>(a_Func);
	}

	template<typename _Ptr,
		typename _Result, 
		typename _Ty, 
		typename _Arg> inline 
		mem_fun1_t<_Result, _Ty, _Arg, _Ptr> 
			mem_fun_ptr(_Result (_Ty::* a_Func)(_Arg))
	{
		return mem_fun1_t<_Result, _Ty, _Arg, _Ptr>(a_Func);
	}

	template<typename _Ptr,
		typename _Result, 
		typename _Ty, 
		typename _Arg> inline 
		const_mem_fun1_t<_Result, _Ty, _Arg, _Ptr> 
			mem_fun_ptr(const _Result(_Ty::* a_Func)(_Arg))
	{
		return const_mem_fun1_t<_Result, _Ty, _Arg, _Ptr>(a_Func);
	}
}
}